#include "stdlib.hpp"
#include <purewell/file.hpp>
#include <purewell/pairfile.hpp>
#include <purewell/iopoll.hpp>

#include <map>
using namespace std;

#include "iopollpoll.hpp"

namespace purewell
{

#if defined(__PW_HAVE_POLL)

PWIOPollPollCont::PWIOPollPollCont() : m_vec(NULL), m_max(0), m_cur(0)
{
}

PWIOPollPollCont::~PWIOPollPollCont()
{
	if ( m_vec )
	{
		delete [] m_vec;
		m_vec = NULL;
	}

	m_max = 0;
	m_cur = 0;
}

bool
PWIOPollPollCont::_increase(void)
{
	if ( m_vec )
	{
		size_t nextsize(m_max*2);
		if ( 0 == nextsize )
		{
			nextsize = m_cur+__PW_POLL_INIT_SIZE;
			if ( nextsize < m_cur )
			{
				_PWTRACE("nextsize(%u) is lower than m_cur(%u)",
					nextsize, m_cur);
				return false;
			}
		}

		poll_t* nextvec(new poll_t[nextsize]);
		if ( !nextvec )
		{
			_PWTRACE("%s", "not enough memory");
			return false;
		}

		memcpy(nextvec, m_vec, sizeof(poll_t)*m_cur);
		delete [] m_vec;
		m_vec = nextvec;
		m_max = nextsize;
	}
	else
	{
		m_vec = new poll_t[__PW_POLL_INIT_SIZE];
		if ( !m_vec )
		{
			_PWTRACE("%s", "not enough memory");
			return false;
		}

		m_cur = 0;
		m_max = __PW_POLL_INIT_SIZE;
	}

	return true;
}

void
PWIOPollPollCont::clear(void)
{
	m_cur = 0;
	m_idx.clear();
}

const PWFile*
PWIOPollPollCont::getFile(int fd) const
{
	index_citr ib(m_idx.find(fd));
	if ( m_idx.end() == ib )
	{
		_PWTRACE("no fd(%d)", fd);
		return NULL;
	}

	return ib->second.pfile;
}

PWFile*
PWIOPollPollCont::getFile(int fd)
{
	index_itr ib(m_idx.find(fd));
	if ( m_idx.end() == ib )
	{
		_PWTRACE("no fd(%d)", fd);
		return NULL;
	}

	return ib->second.pfile;
}

bool
PWIOPollPollCont::add(int fd, PWFile* pfile, int events)
{
	if ( m_idx.find(fd) != m_idx.end() )
	{
		_PWTRACE("no fd(%d)", fd);
		return false;
	}

	if ( m_cur == m_max )
	{
		if ( !_increase() )
		{
			return false;
		}
	}

	poll_t& fdes(m_vec[m_cur]);
	fdes.fd = fd;
	fdes.events = events;

	m_idx.insert(index_cont::value_type(fd, filedes_t(pfile,m_cur)));
	++ m_cur;

	return true;
}

bool
PWIOPollPollCont::remove(int fd)
{
	if ( 0 == m_cur )
	{
		_PWTRACE("%s", "m_cur is zero");
		return false;
	}

	index_itr ib(m_idx.find(fd));
	if ( m_idx.end() == ib )
	{
		_PWTRACE("no fd(%d)", fd);
		return false;
	}

	if ( 1 == m_cur )
	{
		m_cur = 0;
		m_idx.clear();
		return true;
	}

	const size_t idx(ib->second.index);

	size_t lastidx(m_cur-1);
	if ( idx == lastidx )
	{
		-- lastidx;
	}

	const int lastfd(m_vec[lastidx].fd);

	m_vec[idx] = m_vec[lastidx];
	m_idx.find(lastfd)->second.index = idx;

	-- m_cur;

	return true;
}

bool
PWIOPollPollCont::modify(int fd, int events)
{
	index_itr ib(m_idx.find(fd));
	if ( m_idx.end() == ib )
	{
		_PWTRACE("no fd(%d)", fd);
		return false;
	}

	m_vec[ib->second.index].events = events;
	return true;
}

PWIOPollPoll::PWIOPollPoll()
{
}

PWIOPollPoll::~PWIOPollPoll()
{
}

bool
PWIOPollPoll::add(PWPairFile* pfile, size_t idx, int event)
{
	const int fd(pfile->getHandle(idx));
	return m_cont.add(fd, pfile, event);
}

bool
PWIOPollPoll::add(PWFile* pfile, int event)
{
	const int fd(pfile->getHandle());
	return m_cont.add(fd, pfile, event);
}

bool
PWIOPollPoll::modify(PWPairFile* pfile, size_t idx, int event)
{
	const int fd(pfile->getHandle(idx));
	return m_cont.modify(fd, event);
}

bool
PWIOPollPoll::modify(PWFile* pfile, int event)
{
	const int fd(pfile->getHandle());
	return m_cont.modify(fd, event);
}

bool
PWIOPollPoll::modify(int fd, int event)
{
	return m_cont.modify(fd, event);
}

bool
PWIOPollPoll::remove(PWPairFile* pfile, size_t idx)
{
	const int fd(pfile->getHandle(idx));
	return m_cont.remove(fd);
}

bool
PWIOPollPoll::remove(PWFile* pfile)
{
	const int fd(pfile->getHandle());
	return m_cont.remove(fd);
}

bool
PWIOPollPoll::remove(int fd)
{
	return m_cont.remove(fd);
}

bool
PWIOPollPoll::dispatch(int msec)
{
	struct pollfd* pVec(m_cont.getPolls());
	const size_t vecsize(m_cont.getCurrentSize());
	const int ret(poll(pVec, vecsize, msec));
	if ( -1 == ret )
	{
		_PWTRACE("%s",strerror(errno));
		return false;
	}

	int r(0);
	int fd;
	int revents;
	PWFile* pfile(NULL);

	for ( size_t i = 0; i < vecsize && r < ret; i++ )
	{
		revents = pVec[i].revents;
		if ( revents )
		{
			fd = pVec[i].fd;
			pfile = m_cont.getFile(fd);
			if ( pfile )
			{
				pfile->onEvent(this, fd, revents);
				++r;
			}
		}
	}
	
	return true;
}

const char*
PWIOPollPoll::getType(void) const
{
	static const char type[] = "poll";
	return type;
}

bool
PWIOPollPoll::initialize(void* param)
{
	return true;
}

void
PWIOPollPoll::destroy(void)
{
	m_cont.clear();
}

#endif//__PW_HAVE_POLL

};//namespace purewell

